﻿namespace EasyWeb.Test
{
    using System;
    using System.Data.Entity;
    using System.Linq;
    using System.Threading.Tasks;
    using EasyWeb.Configuration;
    using EasyWeb.Data;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class EWEntityDefinitionDao_Test : LibraryTest
    {
        [TestMethod]
        public async Task Select_Test()
        {
            var expecteds = new ew_tbl_entities[2];
            expecteds[0] = CreateRawEntity("test1");
            expecteds[1] = CreateRawEntity("test2");
            await this.TestDbContext.SaveChangesAsync();
            await this.TestDbContext.Entry(expecteds[0]).ReloadAsync();
            await this.TestDbContext.Entry(expecteds[1]).ReloadAsync();

            var actuals = await this.EntityDefinitionDao.SelectAsync();
            Assert.AreEqual(2, actuals.Count());
            foreach (var actual in actuals)
            {
                var expected = expecteds.Single(e => e.ew_uid_entity_id == actual.Id);
                Assert.IsNotNull(expected);

                Assert.AreEqual(expected.ew_uid_entity_id, actual.Id);
                Assert.AreEqual(expected.ew_vc_internal_name, actual.InternalName);
                Assert.AreEqual(expected.ew_xml_display_name, actual.DisplayName.ToText());
                Assert.AreEqual(expected.ew_xml_description, actual.Description.ToText());
                Assert.AreEqual(expected.ew_dt_created.ToUtc(), actual.Created);
                Assert.AreEqual(expected.ew_nvc_created_by, actual.CreatedBy);
                Assert.AreEqual(expected.ew_dt_modified.ToUtc(), actual.Modified);
                Assert.AreEqual(expected.ew_nvc_modified_by, actual.ModifiedBy);
                CollectionAssert.AreEqual(expected.ew_t_timestamp, actual.Timestamp);
            }
        }

        [TestMethod]
        public async Task Create_Test()
        {
            var expected = new EWEntityDefinition()
            {
                InternalName = "test",
                DisplayName = CreateText(),
                Description = CreateText(),
            };
            await this.EntityDefinitionDao.CreateAsync(expected);

            // validate data
            var actual = (await this.TestDbContext.ew_tbl_entities.ToArrayAsync()).ElementAt(0);
            Assert.AreEqual(expected.Id, actual.ew_uid_entity_id);
            Assert.AreEqual(expected.InternalName, actual.ew_vc_internal_name);
            Assert.AreEqual(expected.DisplayName.ToText(), actual.ew_xml_display_name);
            Assert.AreEqual(expected.Description.ToText(), actual.ew_xml_description);
            Assert.AreEqual(expected.Created, actual.ew_dt_created.ToUtc());
            Assert.AreEqual(expected.CreatedBy, actual.ew_nvc_created_by);
            Assert.AreEqual(expected.Modified, actual.ew_dt_modified.ToUtc());
            Assert.AreEqual(expected.ModifiedBy, actual.ew_nvc_modified_by);
            CollectionAssert.AreEqual(expected.Timestamp, actual.ew_t_timestamp);

            // validate table
            await ValidateDatabaseTableAsync("testcase_test");
        }

        [TestMethod]
        public async Task Update_Test()
        {
            var test1 = new EWEntityDefinition()
            {
                InternalName = "test1",
                DisplayName = CreateText(),
                Description = CreateText(),
            };
            await this.EntityDefinitionDao.CreateAsync(test1);
            var actual = (await this.EntityDefinitionDao.SelectAsync()).SingleOrDefault(e => e.Id == test1.Id);
            actual.InternalName = "test2";
            await this.EntityDefinitionDao.UpdateAsync(actual);

            var expected = await this.TestDbContext.ew_tbl_entities.FindAsync(test1.Id);

            // validate data
            Assert.AreEqual(expected.ew_uid_entity_id, actual.Id);
            Assert.AreEqual(expected.ew_vc_internal_name, actual.InternalName);
            Assert.AreEqual(expected.ew_xml_display_name, actual.DisplayName.ToText());
            Assert.AreEqual(expected.ew_xml_description, actual.Description.ToText());
            Assert.AreEqual(expected.ew_dt_created.ToUtc(), actual.Created);
            Assert.AreEqual(expected.ew_nvc_created_by, actual.CreatedBy);
            Assert.AreEqual(expected.ew_dt_modified.ToUtc(), actual.Modified);
            Assert.AreEqual(expected.ew_nvc_modified_by, actual.ModifiedBy);
            CollectionAssert.AreEqual(expected.ew_t_timestamp, actual.Timestamp);

            // validate table
            await ValidateDatabaseTableAsync("testcase_test2");
        }

        [TestMethod]
        public async Task Delete_Test()
        {
            var expected = new EWEntityDefinition()
            {
                InternalName = "test1",
                DisplayName = CreateText(),
                Description = CreateText(),
            };
            await this.EntityDefinitionDao.CreateAsync(expected);

            await this.EntityDefinitionDao.DeleteAsync(expected.Id, expected.Timestamp);

            // validate data
            var actual = await this.TestDbContext.ew_tbl_entities.CountAsync();
            Assert.AreEqual(0, actual);

            // validate table
            var actualTable = (await this.GetDatabaseUserTablesAsync(this.TestDbContext.Database.Connection)).Any(p => p.Key == "testcase_test1");
            Assert.IsFalse(actualTable);
        }

        [TestMethod]
        [ExpectedException(typeof(EWDataException))]
        public async Task CreateDuplicate_Test()
        {
            var expected = new EWEntityDefinition()
            {
                InternalName = "test",
                DisplayName = CreateText(),
                Description = CreateText(),
            };

            await this.EntityDefinitionDao.CreateAsync(expected);
            await this.EntityDefinitionDao.CreateAsync(expected);
        }

        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();

            TestInitializeSystemPrefix();
        }

        private void TestInitializeSystemPrefix()
        {
            var config = EWHelpers.Async.RunSync(async () => await this.ConfigurationDao.SelectAsync<EWSystemPrefixConfiguration>());
            config.Value = "testcase_";
            EWHelpers.Async.RunSync(async () => await this.ConfigurationDao.UpdateAsync(config));
        }

        private ew_tbl_entities CreateRawEntity(string name)
        {
            var item = new ew_tbl_entities()
            {
                ew_uid_entity_id = Guid.NewGuid(),
                ew_dt_created = DateTime.UtcNow,
                ew_nvc_created_by = "test",
                ew_dt_modified = DateTime.UtcNow,
                ew_nvc_modified_by = "test",
                ew_vc_internal_name = name,
                ew_xml_display_name = CreateText().ToText(),
                ew_xml_description = CreateText().ToText()
            };

            this.TestDbContext.ew_tbl_entities.Add(item);

            return item;
        }

        private async Task ValidateDatabaseTableAsync(string tableName)
        {
            var actualTable = (await this.GetDatabaseUserTablesAsync(this.TestDbContext.Database.Connection)).SingleOrDefault(p => p.Key == tableName);
            var actualConstraint = (await this.GetDatabaseUserTableConstraintsAsync(this.TestDbContext.Database.Connection)).SingleOrDefault(p => p.Key == tableName);
            Assert.IsNotNull(actualTable);
            Assert.AreEqual(EWCommon.DatabaseUserTableBuiltInFields.Count, actualTable.Value.Count);
            var idColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTablePrimaryKeyField.Item1);
            Assert.AreEqual("UNIQUEIDENTIFIER", idColumn.Item2, true);
            Assert.IsFalse(idColumn.Item3);
            var createdColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTableCreatedField.Item1);
            Assert.AreEqual("DATETIME2", createdColumn.Item2, true);
            Assert.IsFalse(createdColumn.Item3);
            var createdByColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTableCreatedByField.Item1);
            Assert.AreEqual("NVARCHAR", createdByColumn.Item2, true);
            Assert.IsFalse(createdByColumn.Item3);
            var modifiedColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTableModifiedField.Item1);
            Assert.AreEqual("DATETIME2", modifiedColumn.Item2, true);
            Assert.IsFalse(modifiedColumn.Item3);
            var modifiedByColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTableModifiedByField.Item1);
            Assert.AreEqual("NVARCHAR", modifiedByColumn.Item2, true);
            Assert.IsFalse(modifiedByColumn.Item3);
            var timestampColumn = actualTable.Value.Single(column => column.Item1 == EWCommon.DatabaseUserTableTimestampField.Item1);
            Assert.AreEqual("TIMESTAMP", timestampColumn.Item2, true);
            Assert.IsFalse(timestampColumn.Item3);
            Assert.IsNotNull(actualConstraint);
            Assert.AreEqual(DatabaseTableConstraintTypeName_PrimaryKey, actualConstraint.Value.Single().Item2);
        }

        private static EWText CreateText()
        {
            var text = new EWText();
            text.Values.Add(new EWTextValue("en", "v1"));
            text.Values.Add(new EWTextValue("zh-Hans", "v2"));
            return text;
        }
    }
}
